#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>

#define MAX_SIZE 1024

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Leonard Peng");
MODULE_DESCRIPTION("A simple Linux char driver");
MODULE_VERSION("0.1");

int my_open(struct inode *inode, struct file *file);
int my_release(struct inode *inode, struct file *file);
ssize_t my_read(struct file *file, char __user *user, size_t t, loff_t *f);
ssize_t my_write(struct file *file, const char __user *user, size_t t, loff_t *f);

char message[MAX_SIZE];
int device_num;
const char * const device_name = "myDevice";

struct file_operations pStruct = {
    open: my_open,
    release: my_release,
    read: my_read,
    write: my_write,
};

// register
static int __init my_init_module(void) {
    // 0 表示动态分配主设备号
    device_num = register_chrdev(0, device_name, &pStruct);
    if (device_num< 0) {
        printk("failed to register_chrdev.\n");
        return -1;
    }

    memset(message, 0, MAX_SIZE);
    sprintf(message, "Welcome tom my device");

    printk("succeed to register my devise!\n");
    printk("major device number is %d.\n", device_num);
    return 0;
}

// unregister
static void __exit my_clearnup_module(void) {
    unregister_chrdev(device_num, device_name);
    printk("succeed to unregister.\n");
}

// open
int my_open(struct inode *inode, struct file *file) {
    printk("open my device...\n");
    // 将该模块的引用计数加 1
    try_module_get(THIS_MODULE);
    return 0;
}

// release
int my_release(struct inode *inode, struct file *file) {
    printk("release my device...");
    // 将该模块的引用计数减 1
    module_put(THIS_MODULE);
    return 0;
}

// read
ssize_t my_read(struct file *file, char __user *user, size_t t, loff_t *f) {
    // 由于内核空间与用户空间的内存不能直接互访，
    // 因此借助函数 copy_to_user() 完成用户空间到内核空间的复制
    int size = t > MAX_SIZE ? MAX_SIZE : t;
    if(copy_to_user(user, message, size)) {
        return -2;
    }
    return strlen(message);
}

// write
//向设备里写信息
ssize_t my_write(struct file *file, const char __user *user, size_t t, loff_t *f) {
    int size = t > MAX_SIZE ? MAX_SIZE : t;
    // erase old message
    memset(message, 0, MAX_SIZE);
    if(copy_from_user(message, user, size)) {
        return -3;
    }
    return strlen(message);
}

module_init(my_init_module);
module_exit(my_clearnup_module);
